package com.tender.utils;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JwtTokenManager {

    /**
     * 默认 jwtSecret
     */
    private static String jwtSecret = "wb-csd876509";

    public static void setJwtSecret(String jwtToken) {
        JwtTokenManager.jwtSecret = jwtToken;
    }

    /**
     * 签发令牌：
     * jwt字符串包括三个部分
     * <p>
     * 1. header
     * 一般都是固定的，没有什么变化
     * 1.1、当前字符串的类型，一般都是“JWT”
     * 1.2、哪种算法加密，“HS256”或者其他的加密算法
     * <p>
     * 2. payload
     * 一般有四个最常见的标准字段（下面有）
     * iat：签发时间，也就是这个jwt什么时候生成的
     * jti：JWT的唯一标识
     * iss：签发人，一般都是username或者userId
     * exp：过期时间
     *
     * @param userId     用户id/签发人
     * @param ttlMillis 有效时间
     * @param claims    jwt中存储的一些【非隐私】信息
     */
    public static String issuingToken(String userId, Long ttlMillis, String sessionId, Map<String, Object> claims) {
        if (claims == null) {
            claims = new HashMap<>();
        }
        long nowMillis = System.currentTimeMillis();

        String base64EncodedSecretKey = EncodesUtil.encodeHex(jwtSecret.getBytes());

        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                // jti：JWT的唯一标识
                .setId(sessionId)//2. 这个是JWT的唯一标识，一般设置成唯一的，这个方法可以生成唯一标识,此处存储的为【sessionId】,登录成功后回写
                .setIssuedAt(new Date(nowMillis))//1. 这个地方就是以【毫秒】为单位，换算当前系统时间生成的iat
                .setSubject(userId)//3. 签发人，也就是JWT是给谁的（逻辑上一般都是【username】或者【userId】）
                .signWith(SignatureAlgorithm.HS256, base64EncodedSecretKey);//这个地方是生成jwt使用的算法和秘钥
//                .signWith(SignatureAlgorithm.HS512, base64EncodedSecretKey);//这个地方是生成jwt使用的算法和秘钥
        if (null != ttlMillis && ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);//4. 过期时间，这个也是使用毫秒生成的，使用当前时间+前面传入的持续时间生成
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * @param jwtToken 令牌
     * @return
     * @Description 解析令牌
     */
    public static Claims decodeToken(String jwtToken) {
        String base64EncodedSecretKey = EncodesUtil.encodeHex(jwtSecret.getBytes());

        // 得到 DefaultJwtParser
        return Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(base64EncodedSecretKey)
                // 设置需要解析的 jwt
                .parseClaimsJws(jwtToken)
                .getBody();
    }

    /**
     * @param jwtToken 令牌
     * @return
     * @Description 判断令牌是否合法
     */
    public static boolean verifyToken(String jwtToken) {
        String base64EncodedSecretKey = EncodesUtil.encodeHex(jwtSecret.getBytes());

        // 这个是官方的校验规则，这里只写了一个”校验算法“，可以自己加其它属性的校验，比如 【.withClaim("loginName", "xxxx")】
        Algorithm algorithm = Algorithm.HMAC256(EncodesUtil.decodeBase64(base64EncodedSecretKey));
        JWTVerifier verifier = JWT.require(algorithm)
                // .withClaim("user_name", "lucy")  // 用户名为 lucy
                .build();
        verifier.verify(jwtToken);  // 校验不通过会【抛出异常】

        // 判断合法的标准：
        // 1. 头部和荷载部分【没有被篡改过】
        // 2. 【没有过期】
        return true;
    }


    public static void main(String[] args) {
        String userId = UUID.randomUUID().toString();
        // Long ttlMillis = 5 * 60 * 1000L;
        Long ttlMillis = null;
        String sessionId = "88888888";
        Map<String, Object> claims = new HashMap<>();
        claims.put("user_name", "lucy");
        claims.put("age", 10);
        String jwtToken = issuingToken(userId, ttlMillis, sessionId, claims);
        System.out.println("-----------------------------------------");
        System.out.println("jwtToken = " + jwtToken);
        System.out.println("-----------------------------------------");

        // boolean passFlag = manager.isVerifyToken("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJicnVjZV90ZW5kZXIiLCJ1c2VyX25hbWUiOiJsdWN5IiwiZXhwIjoxNzI0ODk0OTc1LCJpYXQiOjE3MjQ4OTQ2NzUsImFnZSI6MTAsImp0aSI6Ijg4ODg4ODg4In0.ODGoLPxMkz7do3vpJ24NOKYz16Y5iuqaCl8r4tyFzwU");
        boolean passFlag = verifyToken(jwtToken);
        System.out.println("token 校验：" + passFlag);
        System.out.println("-----------------------------------------");

        // 解码 token 中的内容
        Claims result = decodeToken(jwtToken);
        System.out.println(JSON.toJSONString(result));
        System.out.println("-----------------------------------------");
    }

}
